//-----------------------------------------------------------------------------
// Filename: SIPDialogue.cs
//
// Description: Base class for SIP dialogues. 
// 
// History:
// 20 Oct 2005	Aaron Clauson	Created.
//
// License: 
// This software is licensed under the BSD License http://www.opensource.org/licenses/bsd-license.php
//
// Copyright (c) 2006 Aaron Clauson (aaronc@blueface.ie), Blue Face Ltd, Dublin, Ireland (www.blueface.ie)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that 
// the following conditions are met:
//
// Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 
// Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following 
// disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of Blue Face Ltd. 
// nor the names of its contributors may be used to endorse or promote products derived from this software without specific 
// prior written permission. 
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 
// BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 
// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
// POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Threading;
using BlueFace.Sys;
//using BlueFace.VoIP.Authentication;
using log4net;

namespace BlueFace.VoIP.Net.SIP
{
    public enum SIPDialogueStateEnum
    {
        Unknown = 0,
        Early = 1,
        Confirmed = 2,
    }
    
    /// <summary>
    /// See "Chapter 12 Dialogs" in RFC3261.
    /// </summary>
    public class SIPDialogue
	{
		protected static ILog logger = AppSpecificState.logger;

		protected static string m_CRLF = SIPConstants.CRLF;
		protected static string m_sipVersion = SIPConstants.SIP_VERSION_STRING;

        private SIPTransport m_sipTransport;

        public string CallId { get; private set; }
        public SIPRouteSet RouteSet { get; private set; }
        public SIPUserField LocalUserField { get; private set; }     // To header for a UAS, From header for a UAC.
        public int LocalCSeq { get; private set; }                   // CSeq used by the local UA for sending requests. 
        public string LocalTag { get; private set; }
        public SIPUserField RemoteUserField { get; private set; }    // To header for a UAC, From header for a UAS.    
        public string RemoteTag { get; private set; }
        public int RemoteCSeq { get; set; }                          // CSeq being used by the remote UA for sending requests.
        public SIPURI RemoteTarget { get; private set; }             // This will be the Contact URI in the INVITE request or in the 2xx INVITE response and is where subsequent dialogue requests should be sent.
        public string DialogueId { get; private set; }
        //public SIPRouteSet DialogueRouteSet { get; private set; }
        public SIPCDR CDR { get; private set; }                      // Call detail recrod for call the dialogue belongs to.
        public string Owner {get; private set;}                      // In cases where ownership needs to be set on hte dialogue this value can be used. Does not have any effect on the operation of the dialogue and is for info only.
        public string OwnerDomain { get; private set; }
        public string SDP { get; private set; }                     // The sessions description protocol payload. This is not part of or required for the dialogue and is kept for info and consumer app. purposes only.

        private IPEndPoint m_localEndPoint;     // The end point on this proxy that dialog requests should be sent on.

        public SIPDialogueStateEnum DialogueState = SIPDialogueStateEnum.Unknown;

        //public static Int64 Created;
        //public static Int64 Destroyed;

		public SIPDialogue(
            SIPTransport sipTransport,
            string callId, 
            SIPRouteSet routeSet, 
            SIPUserField localUser,
            int localCSeq,
            SIPUserField remoteUser,
            int remoteCSeq,
            SIPURI remoteTarget,
            string localTag,
            string remoteTag,
            IPEndPoint localEndPoint,
            SIPCDR cdr,
            string owner,
            string ownerDomain,
            string sdp)
		{
            //Created++;

            m_sipTransport = sipTransport;

            DialogueId = GetDialogueId(callId, localTag, remoteTag);

            CallId = callId;
            RouteSet = routeSet;
            LocalUserField = localUser;
            LocalCSeq = localCSeq;
            LocalTag = localTag;
            RemoteUserField = remoteUser;
            RemoteTag = remoteTag;
            RemoteCSeq = remoteCSeq;
            RemoteTarget = remoteTarget;
            CDR = cdr;
            Owner = owner;
            OwnerDomain = ownerDomain;
            SDP = sdp;

            m_localEndPoint = localEndPoint;
        }

        public static string GetDialogueId(string callId, string localTag, string remoteTag)
        {
            return Crypto.GetSHAHash(callId + localTag + remoteTag);
        }

        public static string GetDialogueId(SIPHeader sipHeader)
        {
            return Crypto.GetSHAHash(sipHeader.CallId + sipHeader.To.ToTag + sipHeader.From.FromTag);
        }

        public void Hangup()
        {
            //logger.Debug("Hanging up call to " + RemoteTarget.ToString() + ".");

            SIPRequest byeRequest = GetByeRequest(this, m_localEndPoint);
            SIPNonInviteTransaction byeTransaction = m_sipTransport.CreateNonInviteTransaction(byeRequest, byeRequest.GetRequestEndPoint(), m_localEndPoint, RemoteTarget.Protocol);
            byeTransaction.SendReliableRequest();
        }

        private SIPRequest GetByeRequest(SIPDialogue sipDialogue, IPEndPoint sendFromEndPoint)
        {
            SIPRequest byeRequest = new SIPRequest(SIPMethodsEnum.BYE, sipDialogue.RemoteTarget);
            SIPFromHeader byeFromHeader = SIPFromHeader.ParseFromHeader(sipDialogue.LocalUserField.ToString());
            SIPToHeader byeToHeader = SIPToHeader.ParseToHeader(sipDialogue.RemoteUserField.ToString());
            int cseq = sipDialogue.LocalCSeq + 1;

            SIPHeader byeHeader = new SIPHeader(byeFromHeader, byeToHeader, cseq, sipDialogue.CallId);
            byeHeader.CSeqMethod = SIPMethodsEnum.BYE;
            byeRequest.Header = byeHeader;

            if (sipDialogue.RouteSet != null && sipDialogue.RouteSet.Length > 0)
            {
                byeRequest.Header.Routes = sipDialogue.RouteSet.Reversed();
            }

            SIPViaHeader viaHeader = new SIPViaHeader(sendFromEndPoint.Address.ToString(), sendFromEndPoint.Port, CallProperties.CreateBranchId());
            byeRequest.Header.Via.PushViaHeader(viaHeader);

            return byeRequest;
        }

        /*~SIPDialogue()
        {
            Destroyed++;
        }*/
	}
}
